package org.hswebframework.example.crud.web;


import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.AllArgsConstructor;
import org.hswebframework.example.crud.service.*;
import org.hswebframework.ezorm.rdb.mapping.ReactiveRepository;
import org.hswebframework.web.authorization.annotation.QueryAction;
import org.hswebframework.web.crud.web.reactive.ReactiveCrudController;
import org.hswebframework.web.exception.NotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.hswebframework.example.crud.entity.Product;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.*;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.stream.Collectors;

@AllArgsConstructor
@RestController
@RequestMapping("/api/product")
@Tag(name = "简单测试产品")
public class ProductController implements ReactiveCrudController<Product,Long> {

    private final ReactiveRepository<Product, Long> repository;
    @Autowired
    private GroupAggService groupAggService;

    @Override
    public ReactiveRepository<Product, Long> getRepository() {
        return repository;
    }

    @GetMapping("/getProductById")
    @QueryAction
    @Operation(summary = "根据ID查询product")
    public Mono<Product> getTeById(Long id){
        return getRepository()
                .findById(id)
                .switchIfEmpty(Mono.error(NotFoundException::new));
    }

    @GetMapping("/getTop1Products")
    @QueryAction
    @Operation(summary = "根据brand分组第一的producd")
    public Mono<HashMap<String, Optional<Product>>> getTop1ProducByBrand(){
        return getRepository()
                .createQuery()
                .fetch()
                .collectList()
                .map(lst -> {
//                    List<Product> lst = molst.;
                    return lst.stream().collect(Collectors.groupingBy(Product::getBrand, HashMap::new,
                                Collectors.reducing(BinaryOperator.maxBy(
                                        Comparator.comparing(Product::getAmount,Comparator.nullsFirst(Double::compareTo)
                                )))
                    ));
                })
                ;
    }


    @GetMapping("/getTop3Products")
    @QueryAction
    @Operation(summary = "根据brand分组取前三的producds")
    public Mono<Map<String, List<Product>>> getTop3ProducByBrand(){
        return getRepository()
                .createQuery()
                .fetch()
                .collectList()
                .map(lst -> {
                    Map<String,List<Product>> topmp = new HashMap<>();
                    Map<String,List<Product>> mp =  lst.stream().collect(Collectors.groupingBy(Product::getBrand));
                    mp.forEach((k,v)->{
                        List<Product> plst =
                                v.stream()
                                .sorted(Comparator.comparing(Product::getAmount).reversed())
                                .collect(Collectors.toList())
                                .stream()
                                .limit(3)
                                .collect(Collectors.toList());
                        topmp.put(k,plst);
                    });
                    return topmp;
                })
                ;
    }

    @GetMapping("/getTop3Products_1")
    @QueryAction
    @Operation(summary = "根据brand分组取前三的producds")
    public Flux< List<Product>> getTop3ProducByBrand_1(){
        return getRepository()
                .createQuery()
                .fetch()
                .collectList()
                .map(lst -> {
                    Map<String,List<Product>> topmp = new HashMap<>();
                    Map<String,List<Product>> mp =  lst.stream().collect(Collectors.groupingBy(Product::getBrand));
                    mp.forEach((k,v)->{
                        List<Product> plst =
                                v.stream()
                                        .sorted(Comparator.comparing(Product::getAmount).reversed())
                                        .collect(Collectors.toList())
                                        .stream()
                                        .limit(3)
                                        .collect(Collectors.toList());
                        topmp.put(k,plst);
                    });
                    return topmp;
                })
                .flatMapIterable(m->{
                   return m.values().stream().collect(Collectors.toList());
                })
                ;
    }


    @GetMapping("/getCustomGroupTop3Products")
    @QueryAction
    @Operation(summary = "自定义aggregator根据brand分组取前三的producds")
    public Map<Object,Map<String , Object>>  getCustomGroupProducts() throws NoSuchFieldException, IllegalAccessException {

        Map<String, Aggregator<Product>> aggregatorMap = new HashMap<>();

//       Optional<List<Product>>
        final List<Product>[] allProductsList = new List[]{new ArrayList<>()};
                 getRepository()
                .createQuery()
                .fetch()
                .collectList()
//               .block(Duration.ofSeconds(10))
//               .blockOptional()
                .doOnNext(result -> allProductsList[0] = result)
                 .subscribe()
               ;
        List<Product> allProducts = allProductsList[0];
        Comparator<Product> comparator= new Comparator<Product>() {
            @Override
            public int compare(Product o1, Product o2) {
                double diff = o1.getAmount()-o2.getAmount();
                if(diff==0){
                    return 0;
                }
                return diff>0?-1:1;
            }
        };

        Function<Product,Double> sumAmtAggCollect = new Function<Product,Double>() {
            @Override
            public Double apply(Product o) {
                return o.getAmount();
            }
        };
        BinaryOperator<Double> amtSumAggAccumulator = (p1, p2) -> p1+p2;

        Function<Product,Long> QtySumAggCollect = o -> o.getQuantity();
        BinaryOperator<Long> QtySumAggAccumulator = (p1,p2) ->p1+p2;

        SumAggregator sumAmtAgg = new SumAggregator(0.0,amtSumAggAccumulator,sumAmtAggCollect);
        SumAggregator sumQtyAgg = new SumAggregator(0l, QtySumAggAccumulator,QtySumAggCollect );

        TopNAggregator aggregator = new TopNAggregator(comparator,3);
        aggregatorMap.put("count",new CountAggregator<Product>());// 计数
        aggregatorMap.put("top3",aggregator);
        aggregatorMap.put("aggAmt",sumAmtAgg);//统计销售金额
        aggregatorMap.put("aggQty",sumQtyAgg);//统计销量

        Map<Object,Map<String , Object>> aggResults = groupAggService.groupByProperty(allProducts,Product.class,"brand",aggregatorMap);
        return aggResults;
    }



}

